package com.ruoyi.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.dto.convert.DesignConvert;
import com.ruoyi.system.dto.design.DesignDTO;
import com.ruoyi.system.dto.design.DesignQueryDTO;
import com.ruoyi.system.dto.design.UpdateDesignDto;
import com.ruoyi.system.dto.kujiale.PageResponse;
import com.ruoyi.system.dto.kujiale.info.RenderingImage;
import com.ruoyi.system.dto.kujiale.pano.RoamItem;
import com.ruoyi.system.enums.SpaceTypeEnum;
import com.ruoyi.system.mapper.DesignMapper;
import com.ruoyi.system.service.*;
import com.ruoyi.system.utils.PageConvertUtil;
import com.ruoyi.system.utils.SysUserUtils;
import com.ruoyi.system.vo.design.DesignProjectLogVo;
import com.ruoyi.system.vo.design.DesignProjectVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
public class DesignServiceImpl extends ServiceImpl<DesignMapper, Design> implements DesignService {

    private static final List<String> check_File = Lists.newArrayList("living_room_designs", "bedroomDesigns", "diningRoomDesigns", "toilet_designs", "kitchen_designs", "studyRoomDesigns", "balconyDesigns", "atticDesigns", "otherSpaceDesigns");
    @Resource
    private DesignMapper designMapper;

    @Resource
    private ProjectService projectService;


    @Autowired
    private DesignBaseService designBaseService;


    @Autowired
    private DesignSchemeService designSchemeService;

    @Resource
    private DesignSchemeImageService designSchemeImageService;

    @Autowired
    private KuJiaLeService kuJiaLeService;
    /**
     * 日志服务
     */
    @Resource
    private IOperationLogService operationLogService;

    // 同步锁：防止多线程并发时生成重复序号.分布式采用分布式锁
    private final Lock lock = new ReentrantLock();

    // 日期格式化器：固定格式 yyyyMMdd（如 20250908）
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");

    // 编号正则表达式：匹配 SJ-yyyyMMdd-XXX 格式（XXX 为 3 位数字）
    private static final Pattern DESIGN_NO_PATTERN = Pattern.compile("^SJ-(\\d{8})-(\\d{3})$");


    @Override
    public boolean createDesign(DesignDTO designDTO) {
        Design design = DesignConvert.INSTANCE.convert(designDTO);
        design.setDesignNo(generateId());
        design.setCreatedTime(LocalDateTime.now());
        int insert = designMapper.insert(design);
        return insert > 0;
    }

    @Override
    public IPage<DesignProjectVO> getPageList(DesignQueryDTO query) {
        Page<DesignProjectVO> page = new Page<>(query.getPageNum(), query.getPageSize());
        return designMapper.selectDesignProjectPageList(page, query);
    }

    @Override
    public DesignDTO getDesignById(Long id) {
        Design design = designMapper.selectById(id);
        return DesignConvert.INSTANCE.convert(design);
    }


    @Override
    public boolean deleteDesign(Long id) {
        int i = designMapper.deleteById(id);
        return i > 0;
    }

    @Override
    @Transactional
    public boolean updateDesign(Long id, DesignDTO newDesign) {
        Design oldDesign = designMapper.selectById(id);
        if (oldDesign == null || oldDesign.getIsDeleted() == 1) {
            throw new ServiceException("设计不存在或已被删除");
        }
        Design convert = DesignConvert.INSTANCE.convert(newDesign);
        convert.setUpdatedTime(LocalDateTime.now());
        convert.setCreatedTime(null);
        convert.setDesignNo(null);
        convert.setId(id);
        String remark = null;
        Integer isSyncH5Design = 0;
        if (newDesign instanceof UpdateDesignDto) {
            UpdateDesignDto designDTO1 = (UpdateDesignDto) newDesign;
            isSyncH5Design = designDTO1.getIsSyncH5Design();
            remark = designDTO1.getRemark();
        }
        List<OperationLog> operationLogs = Lists.newArrayList();
        // 判断修改状态
        if (newDesign.getDesignStatus() != null && newDesign.getDesignStatus() != oldDesign.getDesignStatus()) {
            OperationLog operationLog = syncDesignStatusChange(oldDesign, oldDesign.getDesignStatus(), newDesign.getDesignStatus(), "SYSTEM", remark, isSyncH5Design);
            operationLogs.add(operationLog);
        }

        JSONObject oldJSON = JSONObject.parseObject(JSON.toJSONString(oldDesign));
        JSONObject newJSON = JSONObject.parseObject(JSON.toJSONString(newDesign));
        for (String s : check_File) {
            List<String> old = Lists.newArrayList();
            try {
                old = oldJSON.getJSONArray(s).toJavaList(String.class);
            } catch (Exception e) {
            }
            List<String> updated = Lists.newArrayList();
            try {
                updated = newJSON.getJSONArray(s).toJavaList(String.class);
            } catch (Exception e) {

            }
            // diff下图纸信息是否有改动
            String operateAction = diffRoomDesigns(old, updated);
            if (operateAction != null) {
                OperationLog operationLog = syncDesignFileUpload(oldDesign.getDesignNo(), s + "变更", operateAction, "SYSTEM", remark, JSON.toJSONString(oldDesign.getLivingRoomDesigns()), JSON.toJSONString(newDesign.getLivingRoomDesigns()), isSyncH5Design);
                operationLogs.add(operationLog);
            }
        }
        boolean b = operationLogService.saveBatch(operationLogs);
        int i = designMapper.updateById(convert);
        return i > 0 && b;
    }

    private String diffRoomDesigns(List<String> old, List<String> update) {
        // 修改后不为空
        if (CollectionUtil.isNotEmpty(update)) {
            // 原来没有
            if (CollectionUtil.isNotEmpty(old)) {
                //上传
                return "FILE_UPLOAD";
            } else {
                if (old.equals(update)) {
                    // 没变化
                    return "";
                }
                // 修改
                return "FILE_UPDATE";
            }
        } else {
            // 修改后为空则表示删除\清空
            if (CollectionUtil.isNotEmpty(old)) {
                return "FILE_DELETE";
            }
        }
        return null;
    }

    /**
     * 生成唯一的设计编号
     *
     * @return 格式：SJ-yyyyMMdd-XXX（如 SJ-20250908-001、SJ-20250908-002）
     */
    public String generateUniqueDesignNo() {
        // 加锁：确保多线程下序号递增唯一
        lock.lock();
        try {
            // 1. 生成当天日期前缀（如 20250908）
            LocalDate today = LocalDate.now();
            String dateStr = today.format(DATE_FORMATTER);
            String prefix = "SJ-" + dateStr + "-"; // 固定前缀：SJ-20250908-

            // 2. 查询数据库中当天已存在的最大编号
            String maxExistingNo = designMapper.selectMaxDesignNoByPrefix(prefix);

            // 3. 计算下一个序号（XXX 部分）
            String nextSequence = calculateNextSequence(maxExistingNo, dateStr);

            // 4. 拼接最终编号
            return prefix + nextSequence;
        } finally {
            // 解锁：确保锁一定会释放，避免死锁
            lock.unlock();
        }
    }


    @Override
    public DesignProjectLogVo getDesignDetailsByUserId(Long h5UserId) {

        Project project = projectService.lambdaQuery()
                .eq(Project::getH5UserId, h5UserId)
                .eq(Project::getDeleted, 0)
                .ne(Project::getProgress, 8)
                .one();

        if (project == null || project.getDeleted() == 1 || StringUtils.isBlank(project.getProjectNo())) {
            throw new ServiceException("项目不存在或已被删除");
        }
        // 项目转JSON
        JSONObject resultJSON = JSON.parseObject(JSON.toJSONString(project));

        // 1. 查询基础设计信息
        DesignBase designBase = designBaseService.lambdaQuery()
                .eq(DesignBase::getProjectId, project.getId())
                .one();
        if (designBase == null) {
            throw new ServiceException("设计信息不存在或未同步到H5");
        }

        // 设计基础
        resultJSON.putAll(JSON.parseObject(JSON.toJSONString(designBase)));

        // 2. 查询该设计下的所有方案
        List<DesignScheme> schemes = designSchemeService.selectByDesignBaseId(designBase.getId());

        if (CollectionUtil.isNotEmpty(schemes)) {
            Optional<DesignScheme> first = schemes.stream().filter(x -> x.getIsAdopted() != null && x.getIsAdopted() == 1).findFirst();
            // 无采纳方案返回
            if (!first.isPresent()) {
                throw new ServiceException("暂无采纳的方案");
            }
            // 添加方案
            DesignScheme scheme = first.get();
            if (scheme.getUseOnlineDesign() != null && scheme.getUseOnlineDesign().equals(1)
                    && StringUtils.isNotBlank(scheme.getOnlineDesignId())) {
                List<DesignSchemeImage> kujialeImg = getKujialeImg(scheme.getOnlineDesignId());
                scheme.setImages(kujialeImg);
                // 查询全屋漫游图
                // 调用服务层获取漫游列表
                Long creatorId = designBase.getCreatorId();
                String kujialeIdBySysUserId = SysUserUtils.getKujialeIdBySysUserId(creatorId);
                if (StringUtils.isNotBlank(kujialeIdBySysUserId)) {
                    List<RoamItem> roamList = kuJiaLeService.getRoamList(kujialeIdBySysUserId, scheme.getOnlineDesignId());
                    if (CollectionUtil.isNotEmpty(roamList)) {
                        List<DesignSchemeImage> roamListResp = getDesignSchemeImages(roamList);
                        if (CollectionUtil.isNotEmpty(scheme.getImages())) {
                            scheme.getImages().addAll(roamListResp);
                        } else {
                            scheme.setImages(roamListResp);
                        }
                    }
                }

            }
            List<String> spaceTypes = scheme.getImages().stream().map(DesignSchemeImage::getSpaceType).filter(StringUtils::isNotBlank).collect(Collectors.toList());
            resultJSON.put("spaceTags", String.join(",", spaceTypes));
            resultJSON.put("spaceTypes", spaceTypes);
            resultJSON.put("scheme", JSON.parseObject(JSON.toJSONString(scheme)));
        }

        DesignProjectLogVo javaObject = resultJSON.toJavaObject(DesignProjectLogVo.class);
        List<OperationLog> logsByBusinessId = operationLogService.getLogsByBusinessIdForH5(designBase.getId().toString());

        if (CollectionUtil.isNotEmpty(logsByBusinessId)) {
            javaObject.setOperationLogList(logsByBusinessId);
        }
        return javaObject;
    }

    /**
     * 酷家乐全屋漫游图转换为images
     *
     * @param roamList
     * @return
     */
    private static List<DesignSchemeImage> getDesignSchemeImages(List<RoamItem> roamList) {
        List<DesignSchemeImage> roamListResp = Lists.newArrayList();
        for (RoamItem roamItem : roamList) {
            DesignSchemeImage designSchemeImage = new DesignSchemeImage();
            designSchemeImage.setSpaceType("roamList");
            designSchemeImage.setSpaceName(roamItem.getPanoType().equals(1) ? "普通漫游图" : "智能漫游图");
            designSchemeImage.setFilePath(roamItem.getCoverUrl());
            designSchemeImage.setFileName(roamItem.getRoamUrl());
            roamListResp.add(designSchemeImage);
        }
        return roamListResp;
    }

    private List<DesignSchemeImage> getKujialeImg(String kujialeDesignId) {
        PageConvertUtil.PageParam convert = PageConvertUtil.convert(1, 50);
        // 明确返回数据类型为 PageResponse，Swagger 可通过 @ApiResponse 关联
        PageResponse<RenderingImage> pageResponse = kuJiaLeService.renderpicList(kujialeDesignId, convert.getStart(), convert.getNum());
        List<RenderingImage> result = pageResponse.getResult();
        if (CollectionUtil.isEmpty(result)) {
            return null;
        }
//        Map<Integer, String> picTypeNameMapeMap = ImmutableMap.of(0, "普通图", 1, "全景图", 3, "俯视图");
        List<DesignSchemeImage> resultList = Lists.newArrayList();

        for (RenderingImage renderingImage : result) {
            DesignSchemeImage designSchemeImage = new DesignSchemeImage();
            resultList.add(designSchemeImage);

            if ( StringUtils.isBlank(renderingImage.getImg())) {
                continue;
            }
            designSchemeImage.setFilePath(renderingImage.getImg());
            SpaceTypeEnum byLabel = SpaceTypeEnum.getByLabel(renderingImage.getRoomName());
            if (byLabel != null) {
                designSchemeImage.setSpaceName(byLabel.getLabel());
                designSchemeImage.setSpaceType(byLabel.getValue());
            } else {
                designSchemeImage.setSpaceName(StringUtils.isBlank(renderingImage.getRoomName()) || renderingImage.getRoomName().equals("未知") ? SpaceTypeEnum.OTHER.getLabel() : renderingImage.getRoomName());
                designSchemeImage.setSpaceType(SpaceTypeEnum.OTHER.getValue());
            }

        }
        return resultList;

    }


    private static final String REDIS_KEY_PREFIX = "id_seq:";

    @Autowired
    private StringRedisTemplate redisTemplate;

    public String generateId() {
        LocalDate currentDate = LocalDate.now();
        String key = REDIS_KEY_PREFIX + currentDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"));

        // Redis 原子自增（默认初始值为 0，需手动设置）
        Long sequence = redisTemplate.opsForValue().increment(key);
        if (sequence == null) {
            // 初始化为 1
            redisTemplate.opsForValue().setIfAbsent(key, "1");
            sequence = 1L;
        }

        return String.format("SJ-%s-%03d", currentDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")), sequence);
    }

    /**
     * 计算下一个序号（XXX 部分，3 位数字，不足补零）
     *
     * @param maxExistingNo 数据库中当天最大的编号（如 SJ-20250908-005）
     * @param todayDateStr  当天日期字符串（如 20250908）
     * @return 下一个序号（如 006）
     */
    private String calculateNextSequence(String maxExistingNo, String todayDateStr) {
        // 情况1：当天无任何编号，从 001 开始
        if (maxExistingNo == null || maxExistingNo.trim().isEmpty()) {
            return "001";
        }

        // 情况2：解析已存在的编号，提取序号
        Matcher matcher = DESIGN_NO_PATTERN.matcher(maxExistingNo);
        if (!matcher.matches()) {
            // 若数据库中存在不符合格式的编号，默认从 001 开始（避免异常）
            return "001";
        }

        // 验证编号的日期是否为当天（防止跨天编号干扰）
        String noDateStr = matcher.group(1); // 提取编号中的日期部分（如 20250908）
        if (!todayDateStr.equals(noDateStr)) {
            return "001";
        }

        // 提取编号中的序号（如 005），转换为整数后加 1
        String existingSeq = matcher.group(2);
        int nextSeq = Integer.parseInt(existingSeq) + 1;

        // 格式化为 3 位数字（不足补零，如 6 → 006，10 → 010，100 → 100）
        return String.format("%03d", nextSeq);
    }

    /**
     * 同步设计项目状态变更到操作日志
     *
     * @param design        设计项目实体（变更后）
     * @param oldStatus     变更前的状态（可为null，新增时无旧状态）
     * @param operateSource 操作来源（PC_WEB/APP/H5/SYSTEM）
     * @param remark        操作备注（可选）
     * @return 构建好的操作日志实体（可直接保存到数据库）
     */
    public OperationLog syncDesignStatusChange(Design design, DesignStatus oldStatus, DesignStatus newStatus, String operateSource, String remark, Integer isSyncH5Design) {

        // 1. 校验必填参数
        Objects.requireNonNull(design, "设计项目实体不能为空");
        Objects.requireNonNull(operateSource, "操作来源不能为空");

        // 2. 构建操作日志实体
        OperationLog log = new OperationLog();

        // 3. 填充操作人信息
        log.setOperateUserId(SecurityUtils.getUserId());
//        log.setOperateUserId(123L);
        log.setOperateUserName(SecurityUtils.getUsername());
//        log.setOperateUserName("admin");
        log.setOperateTime(LocalDateTime.now()); // 操作时间默认为当前时间
        log.setOperateSource(operateSource);

        // 4. 填充业务关联信息（设计项目相关）
        log.setBusinessType("DESIGN"); // 业务类型固定为设计
        log.setBusinessId(design.getDesignNo()); // 关联设计项目编号
        log.setBusinessName(design.getSchemeName()); // 业务名称用方案名称

        // 5. 填充操作动作和状态变更信息
        log.setOperateAction("STATUS_CHANGE"); // 操作动作：状态变更
        log.setBeforeStatus(oldStatus != null ? oldStatus.name() : null); // 变更前状态
        log.setAfterStatus(newStatus.name()); // 变更后状态

        // 6. 填充同步H5设计动态字段（根据业务规则判断是否同步）
        // 示例规则：状态变更为"完成"或"交付"时同步到H5
        /*boolean needSync = design.getDesignStatus() == DesignStatus.COMPLETED
                || design.getDesignStatus() == DesignStatus.FIRST_DELIVERY
                || design.getDesignStatus() == DesignStatus.SECOND_DELIVERY;*/
        log.setIsSyncH5Design(isSyncH5Design);

        // 7. 填充备注（优先使用传入的remark，否则用设计项目的状态备注）
        log.setRemark(remark);

        return log;
    }

    /**
     * 同步设计图纸上传操作到操作日志
     *
     * @param operateSource 操作来源
     * @param remark        上传备注（如"上传客厅设计图V2.0"）
     * @return 构建好的操作日志实体
     */
    public OperationLog syncDesignFileUpload(String designNo, String schemeName, String operateAction, String operateSource, String remark, String oldValue, String newValue, Integer isSyncH5Design) {

        OperationLog log = new OperationLog();

        // 基础信息填充
        log.setOperateUserId(SecurityUtils.getUserId());
        log.setOperateUserName(SecurityUtils.getUsername());
        log.setOperateTime(LocalDateTime.now());
        log.setOperateSource(operateSource);
        log.setBusinessType("DESIGN");
        log.setBusinessId(designNo); // 关联设计项目编号
        log.setBusinessName(schemeName); // 业务名称用方案名称

        log.setBeforeStatus(oldValue);
        log.setAfterStatus(newValue);
        // 操作动作：文件上传
        log.setOperateAction(operateAction);
        log.setRemark(remark != null ? remark : "设计图操作");

        // 图纸上传默认同步到H5设计动态
        log.setIsSyncH5Design(isSyncH5Design);

        return log;
    }

}
